#include "hloop.h"
#include "hsocket.h"
#include "hbase.h"
#include "quickjs-libc.h"
#include "quickjs.h"

#define countof(x) (sizeof(x) / sizeof((x)[0]))

static JSContext *ctx_g;
static int main_isbusy = 0;

unpack_setting_t unpack_setting;

static hloop_t *loop;

/********************  util  ************** */

/**
 * @socket: JSValue socket 传入前需自行DupValue
 */
static void socketListAdd(JSValue list, hio_t *io, JSValue socket)
{
    char key[32] = {0};
    sprintf(key, "%d", hio_fd(io));
    JS_SetPropertyStr(ctx_g, list, key, socket);
}

static JSValue get_socket_from_list(JSValue list, hio_t *io)
{
    char key[32] = {0};
    sprintf(key, "%d", hio_fd(io));
    JSValue socket = JS_GetPropertyStr(ctx_g, list, key);
    return socket;
}

static JSValue js_str_toHex(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    char *str = JS_ToCString(ctx, argv[0]);
    JSValue hex = JS_NewArrayBufferCopy(ctx, str, strlen(str));
    JS_FreeCString(ctx, str);
    return hex;
}

static JSValue js_get_random_udp_port(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    hio_t *io = hloop_create_udp_client(loop, "0.0.0.0", 10086);
    if (!io)
        return JS_ThrowTypeError(ctx, "get_random_udp_port failed");

    const char *test = "helloworld";
    hio_write(io, test, strlen(test));

    char localaddrstr[64] = {0};
    SOCKADDR_STR(hio_localaddr(io), localaddrstr);
    hio_close(io);

    char js_str[100] = {0};
    sprintf(js_str, "(()=>parseInt('%s'.replace('0.0.0.0:','')))()", localaddrstr);
    return JS_Eval(ctx, js_str, strlen(js_str), "<INPUT>", JS_EVAL_TYPE_GLOBAL);
}

static int verificationKey(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, char *key)
{
    // 获取全局对象
    JSValue g = JS_GetGlobalObject(ctx);

    // 检查是否已初始化解密模块
    if (JS_IsUndefined(JS_GetPropertyStr(ctx_g, g, "_decryptisinit")))
    {
#include "jsencrypt.h"

        // 编译并执行解密模块
        JSValue val = JS_Eval(ctx, js, strlen(js), "jscrypt",
                              JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
        if (!JS_IsException(val))
        {
            js_module_set_import_meta(ctx, val, TRUE, TRUE);
            val = JS_EvalFunction(ctx, val);
            JS_SetPropertyStr(ctx, g, "_decryptisinit", JS_NewBool(ctx, 1));
        }
    }

    // 构建解密调用字符串
    char decrypt_js_str[500];
    sprintf(decrypt_js_str, "import { decrypt } from 'jscrypt';  globalThis.tempval = decrypt('%s')", key);
    JS_Eval(ctx, decrypt_js_str, strlen(decrypt_js_str), "<input>", JS_EVAL_TYPE_MODULE);
    JSValue dir_name = JS_GetPropertyStr(ctx, g, "tempval");
    // printf("dir_name: %s\n", JS_ToCString(ctx, dir_name));

    // 构建路径验证代码
    char *get_filename_js =
        "(() => {"
        "try {"
        "    throw new Error();"
        "} catch (error) {"
        "    const filePath = error.stack.split('    at ')[4];"
        "    return ["
        "        '/mods/' + globalThis.tempval + '/',"
        "        '/mods/workshop-' + globalThis.tempval + '/',"
        "        '/322330/' + globalThis.tempval + '/'"
        "    ].some(path => filePath.includes(path));"
        "}"
        "})();";

    // 执行验证
    JSValue passed = JS_Eval(ctx, get_filename_js, strlen(get_filename_js),
                             "<input>", JS_EVAL_TYPE_GLOBAL);

    int result = JS_ToBool(ctx, passed);

    // 清理资源
    JS_FreeValue(ctx, g);
    JS_FreeValue(ctx, dir_name);
    JS_FreeValue(ctx, passed);

    return result;
}

/****************************************** */

static JSValue scoket_list; // 服务端
static JSValue Socket_list; // 客户端
static JSValue server_list; // 服务端
static void on_close(hio_t *io)
{
    printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
    JSValue server = JS_GetPropertyStr(ctx_g, server_list, hio_localaddr(io)->sa_data);
    JS_FreeValue(ctx_g, server);
    char socket_key[32] = {0};
    sprintf(socket_key, "%d", hio_fd(io));
    JSValue scoket = JS_GetPropertyStr(ctx_g, scoket_list, socket_key);
    JSValue close = JS_GetPropertyStr(ctx_g, scoket, "close");
    if (!JS_IsUndefined(close))
    {
        JS_Call(ctx_g, close, JS_UNDEFINED, 0, NULL);
        JS_FreeValue(ctx_g, scoket);
    }
}

static void on_recv(hio_t *io, void *buf, int readbytes)
{
    // printf("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes);
    // char localaddrstr[SOCKADDR_STRLEN] = {0};
    // char peeraddrstr[SOCKADDR_STRLEN] = {0};
    // printf("[%s] <= [%s]\n", SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
    // printf("< %.*s", readbytes, (char *)buf);
    char socket_key[32] = {0};
    sprintf(socket_key, "%d", hio_fd(io));

    JSValue socket = JS_GetPropertyStr(ctx_g, scoket_list, socket_key);
    JSValue ondata = JS_GetPropertyStr(ctx_g, socket, "data");
    if (!JS_IsUndefined(ondata))
    {
        JSValue args[1] = {JS_NewArrayBufferCopy(ctx_g, buf, readbytes)};
        JS_Call(ctx_g, ondata, socket, 1, args);
    }
}
static JSValue ser_write(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    if (JS_IsUndefined(this_val))
        return JS_ThrowTypeError(ctx, "this 丢失");
    if (JS_IsUndefined(JS_GetPropertyStr(ctx, this_val, "fd")))
        return JS_ThrowTypeError(ctx, "fd 未知");
    int fd;
    JS_ToInt32(ctx, &fd, JS_GetPropertyStr(ctx, this_val, "fd"));
    hio_t *io = hio_get(loop, fd);
    size_t len;
    JSValue js_buf = JS_DupValue(ctx, argv[0]);
    char *buf = JS_GetArrayBuffer(ctx, &len, js_buf);
    if (!buf)
    {
        JS_FreeValue(ctx, js_buf);
        return JS_ThrowTypeError(ctx, "必须传入 ArrayBuffer");
    }
    hio_write(io, buf, len);
    JS_FreeValue(ctx, js_buf);
    return JS_UNDEFINED;
}

static void on_read(hio_t *io, void *buf, int readbytes)
{
    // printf("tcp on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes);
    // char localaddrstr[SOCKADDR_STRLEN] = {0};
    // char peeraddrstr[SOCKADDR_STRLEN] = {0};
    // printf("%s <= %s\n", SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
    // printf("< %.*s", readbytes, (char *)buf);
    char socket_key[32] = {0};
    sprintf(socket_key, "%d", hio_fd(io));

    JSValue socket = JS_DupValue(ctx_g, JS_GetPropertyStr(ctx_g, Socket_list, socket_key));
    JSValue ondata = JS_DupValue(ctx_g, JS_GetPropertyStr(ctx_g, socket, "data"));

    if (!JS_IsUndefined(ondata))
    {
        if (!JS_IsFunction(ctx_g, ondata))
        {
            perror("ondata is not a function\n");
            return;
        }
        JSValue js_buf = JS_DupValue(ctx_g, JS_NewArrayBufferCopy(ctx_g, buf, readbytes));
        JSValue args[1] = {js_buf};

        JS_Call(ctx_g, ondata, socket, 1, args);
        JS_FreeValue(ctx_g, js_buf);
        JS_FreeValue(ctx_g, socket);
        JS_FreeValue(ctx_g, ondata);
    }
}

JSValue socket_on(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
{
    const char *on_name = JS_ToCString(ctx, argv[0]);
    JS_SetPropertyStr(ctx, this_val, on_name, JS_DupValue(ctx, argv[1]));

    JS_FreeCString(ctx, on_name);
    return JS_UNDEFINED;
}

static void on_accept(hio_t *io)
{
    JSValue server = JS_GetPropertyStr(ctx_g, server_list, hio_localaddr(io)->sa_data);
    JSValue _connectionListener = JS_GetPropertyStr(ctx_g, server, "connectionListener");

    JSValue socket_obj = JS_NewObject(ctx_g);
    JS_SetPropertyStr(ctx_g, socket_obj, "on", JS_NewCFunction(ctx_g, socket_on, "on", 2));
    JS_SetPropertyStr(ctx_g, socket_obj, "fd", JS_NewInt32(ctx_g, hio_fd(io)));
    JS_SetPropertyStr(ctx_g, socket_obj, "write", JS_NewCFunction(ctx_g, ser_write, "write", 1));

    JSValue args[1] = {socket_obj};
    JS_Call(ctx_g, _connectionListener, server, 1, args);

    // printf("on_accept connfd=%d\n", hio_fd(io));
    // char localaddrstr[SOCKADDR_STRLEN] = {0};
    // char peeraddrstr[SOCKADDR_STRLEN] = {0};
    // printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io), SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
    char socket_key[32] = {0};
    sprintf(socket_key, "%d", hio_fd(io));
    JS_SetPropertyStr(ctx_g, scoket_list, socket_key, socket_obj);

    hio_setcb_read(io, on_recv);
    hio_setcb_close(io, on_close);
    hio_read_start(io);
    printf("hio_read_start fd=%d\n", hio_fd(io));

    hio_set_unpack(io, &unpack_setting);
}

static hio_t *dispath_io = NULL;
static void on_accept_dispath(hio_t *io)
{
    printf("listenfd=%d,%d,%s\n", hio_fd(io), hio_id(io), hio_localaddr(io)->sa_data);
    while (1)
    {
        if (!dispath_io)
            break;
        /* code */
    }
    dispath_io = io;
}

static void initscoket() {}

static JSValue listen_fn(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
{
    // 参数验证
    if (argc < 3)
    {
        return JS_ThrowTypeError(ctx, "Invalid arguments");
    }

    // 验证密钥
    JSValue key_jsvalue = JS_DupValue(ctx, argv[0]);
    char *key = JS_ToCString(ctx, key_jsvalue);
    if (!key)
    {
        JS_FreeValue(ctx, key_jsvalue);
        return JS_ThrowTypeError(ctx, "Invalid key");
    }

    int canuse = verificationKey(ctx, this_val, argc, argv, key);
    JS_FreeValue(ctx, key_jsvalue);
    JS_FreeCString(ctx, key);

    if (!canuse)
    {
        return JS_ThrowTypeError(ctx, "这看起来很糟糕，我不会这样做");
    }

    int islisten = JS_ToBool(ctx, JS_GetPropertyStr(ctx, this_val, "islisten"));
    if (islisten)
        return JS_ThrowTypeError(ctx, "已经设置过监听了");
    int port;
    JS_ToInt32(ctx, &port, argv[1]);
    char *ip = JS_ToCString(ctx, argv[2]);
    printf("参数：（%d）(%d)(%s)", argc, port, ip);

    // 创建TCP服务
    hio_t *listenio = hloop_create_tcp_server(loop, ip, port, on_accept);
    if (listenio == NULL)
    {
        JS_FreeCString(ctx, ip);
        return JS_ThrowTypeError(ctx, strerror(errno));
    }
    printf("listenfd=%d,%d,%s\n", hio_fd(listenio), hio_id(listenio), hio_localaddr(listenio)->sa_data);
    JS_SetPropertyStr(ctx, server_list, hio_localaddr(listenio)->sa_data, JS_DupValue(ctx, this_val)); // 已设置释放点
    JS_SetPropertyStr(ctx, this_val, "_hio_t", JS_NewInt64(ctx, (int)listenio));

    JS_FreeCString(ctx, ip);
    return JS_UNDEFINED;
}

static JSValue execute_js_module(JSContext *ctx, const char *module_code)
{
    // 编译模块
    JSValue compiled_module = JS_Eval(ctx, module_code, strlen(module_code), "<module>", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
    if (JS_IsException(compiled_module))
    {
        // 处理编译错误
        JSValue exception = JS_GetException(ctx);
        const char *error_str = JS_ToCString(ctx, exception);
        printf("Compilation error: %s\n", error_str);
        JS_FreeCString(ctx, error_str);
        JS_FreeValue(ctx, exception);
        return JS_UNDEFINED;
    }

    // 执行模块
    JSValue result = JS_EvalFunction(ctx, compiled_module);
    if (JS_IsException(result))
    {
        // 处理执行错误
        JSValue exception = JS_GetException(ctx);
        const char *error_str = JS_ToCString(ctx, exception);
        printf("Execution error: %s\n", error_str);
        JS_FreeCString(ctx, error_str);
        JS_FreeValue(ctx, exception);
    }

    return result;
}

static JSValue js_createServer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    // int canuse = verificationKey(ctx, this_val, 0, NULL, "XuNgAtbMZJ/xS39Vno7ecIR/JbRIaXezVc1tXvsEQDn2xDoZ+k0vaO2Aikv5jKKLEmvbSkIUURT64x9VSfKe3pvKw/GRR/s5nZG8NKagqj+fXSy+YRah8ZDndg5xGJoVa3AZ7K4A+m/IiedF72JXM9/s4gxo17fOdHpRX95vfqU=");
    // printf("canuse: %d\n", canuse);
    JSValue server = JS_NewObject(ctx);
    JS_SetPropertyStr(ctx, server, "islisten", JS_NewBool(ctx, 0));
    JS_SetPropertyStr(ctx, server, "connectionListener", JS_DupValue(ctx_g, argv[0]));
    JSValue listen = JS_NewCFunction(ctx, listen_fn, "listen", 2);
    JS_SetPropertyStr(ctx, server, "listen", listen);
    return server;
}

static void on_connect(hio_t *io)
{
    char socket_key[32] = {0};
    sprintf(socket_key, "%d", hio_fd(io));
    JSValue socket = JS_GetPropertyStr(ctx_g, Socket_list, socket_key);
    JSValue connectedcall = JS_GetPropertyStr(ctx_g, socket, "connected");
    JS_Call(ctx_g, connectedcall, socket, 0, NULL);

    hio_set_unpack(io, &unpack_setting);
    hio_setcb_read(io, on_read);
    hio_read_start(io);
}
static void on_connect_close(hio_t *io)
{
    char socket_key[32] = {0};
    sprintf(socket_key, "%d", hio_fd(io));
    JSValue socket = JS_GetPropertyStr(ctx_g, Socket_list, socket_key);
    JSValue close = JS_GetPropertyStr(ctx_g, socket, "close");
    JS_Call(ctx_g, close, socket, 0, NULL);
}

static JSValue js_write(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    if (JS_IsUndefined(this_val))
        return JS_ThrowTypeError(ctx, "this 丢失");
    if (JS_IsUndefined(JS_GetPropertyStr(ctx, this_val, "fd")))
        return JS_ThrowTypeError(ctx, "fd 未知");
    int fd;
    JS_ToInt32(ctx, &fd, JS_GetPropertyStr(ctx, this_val, "fd"));
    hio_t *io = hio_get(loop, fd);
    size_t len;
    JSValue js_buf = JS_DupValue(ctx, argv[0]);
    uint8_t *buf = JS_GetArrayBuffer(ctx, &len, js_buf);
    hio_write(io, buf, len);
    JS_FreeValue(ctx, js_buf);
    return JS_UNDEFINED;
}

static JSValue tcp_client_destroy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    JSValue fd_jsvalue = JS_GetPropertyStr(ctx, this_val, "fd");
    int fd;
    JS_ToInt32(ctx, &fd, fd_jsvalue);
    hio_close(hio_get(loop, fd));
    return JS_UNDEFINED;
}

static JSValue connect_fn(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    // 参数验证
    if (argc < 4)
    {
        return JS_ThrowTypeError(ctx, "Invalid arguments: connect(key, port, ip, callback)");
    }

    // 验证密钥
    JSValue key_jsvalue = JS_DupValue(ctx, argv[0]);
    char *key = JS_ToCString(ctx, key_jsvalue);
    if (!key)
    {
        JS_FreeValue(ctx, key_jsvalue);
        return JS_ThrowTypeError(ctx, "Invalid key");
    }

    int canuse = verificationKey(ctx, this_val, argc, argv, key);
    JS_FreeValue(ctx, key_jsvalue);
    JS_FreeCString(ctx, key);

    if (!canuse)
    {
        return JS_ThrowTypeError(ctx, "这看起来很糟糕，我不会这样做");
    }

    // 获取端口和IP
    int port;
    if (JS_ToInt32(ctx, &port, argv[1]) < 0)
    {
        return JS_ThrowTypeError(ctx, "Invalid port number");
    }

    char *ip = JS_ToCString(ctx, argv[2]);
    if (!ip)
    {
        return JS_ThrowTypeError(ctx, "Invalid IP address");
    }

    // 保存回调函数
    JSValue connected_cb = JS_DupValue(ctx, argv[3]);
    JS_SetPropertyStr(ctx, this_val, "connected", connected_cb);

    // 创建TCP客户端
    hio_t *io = hloop_create_tcp_client(loop, ip, port, on_connect, on_connect_close);
    JS_FreeCString(ctx, ip);

    if (io == NULL)
    {
        return JS_ThrowTypeError(ctx, "Failed to create TCP client");
    }

    // 设置socket属性
    JS_SetPropertyStr(ctx, this_val, "destroy",
                      JS_NewCFunction(ctx, tcp_client_destroy, "destroy", 0));
    JS_SetPropertyStr(ctx, this_val, "write",
                      JS_NewCFunction(ctx, js_write, "write", 1));
    JS_SetPropertyStr(ctx, this_val, "fd",
                      JS_NewInt32(ctx, hio_fd(io)));

    // 保存socket引用
    char socket_key[32];
    snprintf(socket_key, sizeof(socket_key), "%d", hio_fd(io));
    JS_SetPropertyStr(ctx, Socket_list, socket_key, JS_DupValue(ctx, this_val));

    return JS_UNDEFINED;
}

static JSValue js_Socket(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    JSValue socket = JS_NewObject(ctx);
    JS_SetPropertyStr(ctx, socket, "connect", JS_NewCFunction(ctx, connect_fn, "connect", 1));
    JS_SetPropertyStr(ctx, socket, "on", JS_NewCFunction(ctx, socket_on, "on", 2));
    return socket;
}

extern int hloop_run_while(hloop_t *loop);
int hloop_run_while_native()
{
    if (loop)
        hloop_run_while(loop);
    return 0;
}
static JSValue jhloop(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{

    hloop_run_while(loop);
    if (dispath_io)
    {
        on_accept(dispath_io);
        dispath_io = NULL;
    }

    return JS_UNDEFINED;
}

/********************  udp  ************** */
JSValue udp_socket_list;
static JSValue udp_bind(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    // no need
}

static void udp_read(hio_t *io, void *buf, int readbytes)
{
    // printf("udp_read:(fd:%d)  \n ", hio_fd(io));
    // char localaddrstr[SOCKADDR_STRLEN] = {0};
    char peeraddrstr[SOCKADDR_STRLEN] = {0};
    // printf("[%s] <=> [%s]\n", SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
    //  printf("< %.*s", readbytes, (char *)buf);

    // printf("%s\n%s\n", localaddrstr, peeraddrstr);
    SOCKADDR_STR(hio_peeraddr(io), peeraddrstr);
    char js[150] = {0};
    sprintf(js, "(()=>{ const to = '%s'; const s = to.split(':'); return{address:s[0],port:s[1]};})();", peeraddrstr);
    JSValue rinfo = JS_Eval(ctx_g, js, strlen(js), "<INPUT>", JS_EVAL_TYPE_GLOBAL);

    JSValue socket = get_socket_from_list(udp_socket_list, io);
    JSAtom mesgat = JS_NewAtom(ctx_g, "message");

    if (JS_HasProperty(ctx_g, socket, mesgat))
    {
        JSValue message = JS_GetPropertyStr(ctx_g, socket, "message");
        JSValue buff = JS_NewArrayBufferCopy(ctx_g, buf, readbytes);
        JSValue args[2] = {buff, rinfo};
        JS_Call(ctx_g, message, socket, 2, args);
        JS_FreeValue(ctx_g, buff);
    }
    JS_FreeAtom(ctx_g, mesgat);
    JS_FreeValue(ctx_g, rinfo);
}

static JSValue udp_send(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    int fd;
    JS_ToInt32(ctx, &fd, JS_GetPropertyStr(ctx, this_val, "fd"));
    JSValue data = JS_DupValue(ctx, argv[0]);
    size_t len;
    uint8_t *buf = JS_GetArrayBuffer(ctx, &len, data);

    hio_t *io = hio_get(loop, fd);
    // printf("send :%d\n", fd);

    if (!JS_IsUndefined(argv[1]) && !JS_IsUndefined(argv[2]))
    {
        int port;
        JS_ToInt32(ctx, &port, argv[1]);
        char *host = JS_ToCString(ctx, argv[2]);
        sockaddr_u addr;
        sockaddr_set_ipport(&addr, host, port);
        // hio_set_peeraddr(io, &addr.sa, sockaddr_len(&addr));

        // char peeraddrstr[SOCKADDR_STRLEN] = {0};
        // printf("重定向： %s\n", SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
        JS_FreeCString(ctx, host);
    }

    hio_write(io, buf, len);

    JS_FreeValue(ctx, data);
    return JS_UNDEFINED;
}

static JSValue udp_close(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    JSValue fd_jsvalue = JS_GetPropertyStr(ctx, this_val, "fd");
    int fd;
    JS_ToInt32(ctx, &fd, fd_jsvalue);
    hio_t *io = hio_get(loop, fd);
    hio_close(io);
    return JS_UNDEFINED;
}

static JSValue js_createSocket(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    // 验证密钥
    JSValue key_jsvalue = JS_DupValue(ctx, argv[0]);
    char *key = JS_ToCString(ctx, key_jsvalue);
    if (!key)
    {
        JS_FreeValue(ctx, key_jsvalue);
        return JS_ThrowTypeError(ctx, "Invalid key");
    }

    int canuse = verificationKey(ctx, this_val, argc, argv, key);
    JS_FreeValue(ctx, key_jsvalue);
    JS_FreeCString(ctx, key);

    if (!canuse)
    {
        return JS_ThrowTypeError(ctx, "这看起来很糟糕，我不会这样做");
    }

    JSValue socket = JS_NewObject(ctx);

    char *udptype = JS_ToCString(ctx, argv[1]);
    char *host = JS_ToCString(ctx, argv[2]);
    int port;
    JS_ToInt32(ctx, &port, argv[3]);
    hio_t *io;
    int fd;
    if (strcmp(udptype, "client") == 0)
    {
        io = hloop_create_udp_client(loop, host, port);
        if (io == NULL)
        {
            JS_FreeCString(ctx, host);
            JS_FreeCString(ctx, udptype);
            return JS_ThrowTypeError(ctx, "创建失败");
        }
        fd = hio_fd(io);
        JS_SetPropertyStr(ctx, socket, "fd", JS_NewInt32(ctx, fd));
        socketListAdd(udp_socket_list, io, JS_DupValue(ctx, socket));
    }
    else if (strcmp(udptype, "server") == 0)
    {
        io = hloop_create_udp_server(loop, host, port);
        if (io == NULL)
        {
            JS_FreeCString(ctx, host);
            JS_FreeCString(ctx, udptype);
            return JS_ThrowTypeError(ctx, "创建失败");
        }
        fd = hio_fd(io);
        JS_SetPropertyStr(ctx, socket, "fd", JS_NewInt32(ctx, fd));
        socketListAdd(udp_socket_list, io, JS_DupValue(ctx, socket));
    }
    else
    {
        JS_FreeCString(ctx, host);
        JS_FreeCString(ctx, udptype);
        return JS_ThrowTypeError(ctx, "invalid socket type use 'client' or 'server'");
    }

    hio_setcb_read(io, udp_read);

    JS_SetPropertyStr(ctx, socket, "io", JS_NewInt32(ctx, (int64_t)io));
    JS_SetPropertyStr(ctx, socket, "on", JS_NewCFunction(ctx, socket_on, "on", 2));
    JS_SetPropertyStr(ctx, socket, "send", JS_NewCFunction(ctx, udp_send, "send", 3));
    JS_SetPropertyStr(ctx, socket, "close", JS_NewCFunction(ctx, udp_close, "close", 0));

    hio_read_start(io);
    JS_FreeCString(ctx, host);
    JS_FreeCString(ctx, udptype);
    return socket;
}

/********************   ******************* */

static void *mainthread()
{

    memset(&unpack_setting, 0, sizeof(unpack_setting_t));
    unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
    unpack_setting.mode = UNPACK_BY_DELIMITER;
    unpack_setting.delimiter[0] = '\n';
    unpack_setting.delimiter_bytes = 1;

    hloop_run(loop);
    hloop_free(&loop);
    return NULL;
}

/* 定义API的函数入口名称及列表 */
static const JSCFunctionListEntry js_jhscoket_funcs[] = {
    /* JS_CFUNC_DEF(函数入口名称，入参个数，QuickJS C 函数) */
    JS_CFUNC_DEF("createServer", 0, js_createServer),
    JS_CFUNC_DEF("loop", 0, jhloop),
    JS_CFUNC_DEF("Socket", 0, js_Socket),
    JS_CFUNC_DEF("createSocket", 3, js_createSocket),
    JS_CFUNC_DEF("str_toHex", 1, js_str_toHex),
    JS_CFUNC_DEF("get_random_udp_port", 0, js_get_random_udp_port),
};
// #define JS_INIT_MODULE
extern int hloop_run_begin(hloop_t *loop);
static int js_jhsocket_init(JSContext *ctx, JSModuleDef *m)
{
    return JS_SetModuleExportList(ctx, m, js_jhscoket_funcs, countof(js_jhscoket_funcs));
}
JSModuleDef *js_init_module_socket(JSContext *ctx, const char *module_name)
{
    ctx_g = ctx;
    JSModuleDef *m;
    m = JS_NewCModule(ctx, module_name, js_jhsocket_init);
    if (!m)
        return NULL;
    JS_AddModuleExportList(ctx, m, js_jhscoket_funcs, countof(js_jhscoket_funcs));

    server_list = JS_NewObject(ctx);
    scoket_list = JS_NewObject(ctx);
    Socket_list = JS_NewObject(ctx);
    udp_socket_list = JS_NewObject(ctx);

    loop = hloop_new(0);

    memset(&unpack_setting, 0, sizeof(unpack_setting_t));
    unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
    unpack_setting.mode = UNPACK_BY_DELIMITER;
    unpack_setting.delimiter[0] = '\n';
    unpack_setting.delimiter_bytes = 1;

    hloop_run_begin(loop);

    return m;
}
